/*
 *  homedepot.c
 *  
 *
 *  Created by Rayvionne French on 6/15/11.
 *  Copyright 2011 __MyCompanyName__. All rights reserved.
 *
 */
 
#include "syscall.h"

#define MAXCLERKS 5
#define MAXSTOCKBOYS 10
#define MAXAISLES 5
#define MAXSHELVES 10
#define MAXSHELFQUANTITY 3
#define MAXCARTSIZE 10
#define SHELVESPERAISLE 2

typedef enum 
{
	FALSE = 0, 
	TRUE = 1
} boolean;

typedef enum { 
	NONE,
	REPLENISHING
} shelfState;

typedef struct {
	int productID;
	int price;
} Product;

typedef struct {
	shelfState state; 
	int aisleNumber;
	int shelfItemType; 
	int shelfItemPrice;
	Product* products[MAXSHELFQUANTITY];
	int currentItemCount;
} Shelf;


int globalLock;

/* Customer data */
int customerCount = 0;
int clerkCount = 0;
int cashRegisterCount = 0;
int stockboyCount = 0;

/* To keep track of total customers served */
int totalCustomersServed = 0;
/* The actual store total, not the manager's estimate */
int storeTotal = 0;

/* Home depot's lock for each shelf */
int shelfLocks[MAXSHELVES];
/* Home depot's Products */
Shelf shelves[MAXSHELVES];

/* Represents the line length for each clerk (each index) */
int clerkLineLength[MAXCLERKS];
/* Clerk line lock ensures that only one instance can check/increment/decrement the line count at a time.*/
int clerkLineLock;
/* For customers waiting in lines */
int clerkLineCV[MAXCLERKS];

/* Lock that protects the checkoutAssistance button */
int checkoutAssistanceLock;
/* Button that is pushed when a customer sees that all clerks are ONBREAK.
 * TRUE is need help, FALSE if no help needed */
boolean checkoutAssistance;
/* ConditionVar that will wake up customers when the checkout assistance request
 * has been fulfilled */
int checkoutAssistanceCV;

/* The customer must have a way to know whether the clerk
 * is on break; 0 is working, 1 is on break.*/
int clerkState[MAXCLERKS];
int clerkLocks[MAXCLERKS];
int clerkCV[MAXCLERKS];

boolean clerkAssistanceButton;
int clerkAssistanceButtonLock;

/* Lock for knowing which customer are currently being served - by clerk */
int customerAtCheckoutLocks[MAXCLERKS];
int customerAtCheckoutCV[MAXCLERKS];
int customerAtCheckout[MAXCLERKS];

boolean refundingTransaction[MAXCLERKS];

int paymentDue[MAXCLERKS];
int amountPaid[MAXCLERKS];

boolean productScanningFinished[MAXCLERKS];
int productToScan[MAXCLERKS];

boolean cashRegisterFinishedStatus[MAXCLERKS]; 
int cashRegisterSales[MAXCLERKS];
int cashRegisterAmount[MAXCLERKS];
int cashRegisterLocks[MAXCLERKS];
int cashRegisterCV[MAXCLERKS];

int cashRegisterAssistance[MAXCLERKS];
int cashRegisterAssistanceCV[MAXCLERKS];
int cashRegisterAssistanceLock;
int cashRegisterRequest[MAXCLERKS];

/* Lock for changing aisle location of a stockboy */
int stockboyStatusLock;
/* for current aisle location, -1 means currently not in any aisle */
int stockboyStatus[MAXSTOCKBOYS]; 

/* Lock for the stockroom */
int stockroomLineLock;
int stockroomLineCV;
int stockroomLineLength = 0;

int stockroomLock;
int stockboyInStockroom;

/* Lock for stockboys */
int stockboyLocks[MAXSTOCKBOYS];
/* State of a stockboy: 0 is working, 1 is on break */
int stockboyState[MAXSTOCKBOYS];
/* Condition variable for a stockboy */
int stockboysCV[MAXSTOCKBOYS];

int stockboyReadyLock[MAXAISLES];
int aisleHelpLine[MAXAISLES];
int aisleHelpLocks[MAXAISLES];
int aisleCV[MAXAISLES];

int customerWithStockboyLock[MAXSTOCKBOYS];
int customerWithStockboy[MAXSTOCKBOYS];
int stockboyProductToHelp[MAXSTOCKBOYS];
int shelfNumberForCustomer[MAXSTOCKBOYS];
int aisleNumberForCustomer[MAXSTOCKBOYS];

/* Lock for customer & manager to access this button */
int customerAssistanceButtonLock;
/* Customer presses on this button if no stockboys can be found */
boolean customerAssistanceButton;
/* CV for customers to wait on when the manager wakes up all stockboys */
int customerAssistanceButtonCV;



/* Takes in a char array and returns the length of the char array. */
int charCount(char* ch)
{	
	char toString[3];
	int count = 0;
	int i=0;
	while(ch[i] != '\n')
	{
		count++;
		i++;
	}
	return count+1;
}

void Initialize() {
	int j, k, m;
	
	globalLock = CreateLock("globalLock", 10);
	stockboyInStockroom = -1;
	
	for(j=0; j<MAXCLERKS; j++) {
		clerkLineLength[j] = 0;
		if(j==0)
			clerkState[j] = 0;
		else
			clerkState[j] = 1;
		paymentDue[j] = 0;
		productScanningFinished[j] = FALSE;
		productToScan[j]= -1;
		cashRegisterFinishedStatus[j] = FALSE;
		customerAtCheckout[j] = -1;
		cashRegisterSales[j] = 0;
		cashRegisterAmount[j] = 0;
		cashRegisterAssistance[j] = 0;
		cashRegisterRequest[j] = 0;
		refundingTransaction[j] = FALSE;
	}
	/*Write("MAXCLERKS loop done.\n", charCount("MAXCLERKS loop done.\n"), ConsoleOutput);*/
	
	m=0;
	for(j=0; j < MAXSHELVES; j++) {
		shelves[j].state = NONE;
		shelves[j].aisleNumber = m;
		shelves[j].shelfItemType = j; 
		shelves[j].shelfItemPrice = 1;
		shelves[j].currentItemCount = MAXSHELFQUANTITY;
		for(k=0; k<MAXSHELFQUANTITY;k++) {
			shelves[j].products[k]->productID = j;
			shelves[j].products[k]->price = shelves[j].shelfItemPrice;
		}
	
		if(j == 1 || j == 3 || j == 5 || j == 7)
			m++;
	}
	
	/*Write("MAXSHELVES loop done.\n", charCount("MAXSHELVES loop done.\n"), ConsoleOutput);*/
	
	for(j=0; j < MAXSTOCKBOYS; j++) {
		stockboyStatus[j] = -1;
		stockboyState[j] = 1;
		stockboyProductToHelp[j] = -1;
		shelfNumberForCustomer[j] = -1;
		customerWithStockboy[j] = -1;
		stockboyProductToHelp[j] = -1;
		shelfNumberForCustomer[j] = -1;
		aisleNumberForCustomer[j] = -1;
	}
	
	/*Write("MAXSTOCKBOYS loop done.\n", charCount("MAXSTOCKBOYS loop done.\n"), ConsoleOutput);*/
	
	for(j=0; j < MAXAISLES; j++) {
		aisleHelpLine[j] = 0;
	}
	
	clerkLineLock = CreateLock("clerkLineLock", 13);
	
	clerkLineCV[0] = CreateCV("clerkLineCV0", 12);
	clerkLineCV[1] = CreateCV("clerkLineCV1", 12);
	clerkLineCV[2] = CreateCV("clerkLineCV2", 12);
	clerkLineCV[3] = CreateCV("clerkLineCV3", 12);
	clerkLineCV[4] = CreateCV("clerkLineCV4", 12);
	
	/*Write("CLERK LINE DATA done.\n", charCount("CLERK LINE DATA done.\n"), ConsoleOutput);*/
	
	clerkLocks[0] = CreateLock("clerkLock0", 10);
	clerkLocks[1] = CreateLock("clerkLock1", 10);
	clerkLocks[2] = CreateLock("clerkLock2", 10);
	clerkLocks[3] = CreateLock("clerkLock3", 10);
	clerkLocks[4] = CreateLock("clerkLock4", 10);
	
	clerkCV[0] = CreateCV("clerkCV0", 8); 
	clerkCV[1] = CreateCV("clerkCV1", 8);
	clerkCV[2] = CreateCV("clerkCV2", 8);
	clerkCV[3] = CreateCV("clerkCV3", 8);
	clerkCV[4] = CreateCV("clerkCV4", 8);
	
	clerkAssistanceButton = FALSE;
	clerkAssistanceButtonLock = CreateLock("clerkAssistanceButton", 21);
	
	/*Write("CLERK DATA done.\n", charCount("CLERK DATA done.\n"), ConsoleOutput);*/
	
	cashRegisterLocks[0] = CreateLock("registerLock0", 13);
	cashRegisterLocks[1] = CreateLock("registerLock1", 13);
	cashRegisterLocks[2] = CreateLock("registerLock2", 13);
	cashRegisterLocks[3] = CreateLock("registerLock3", 13);
	cashRegisterLocks[4] = CreateLock("registerLock4", 13);
	
	cashRegisterCV[0] = CreateCV("registerCV0", 11);
	cashRegisterCV[1] = CreateCV("registerCV1", 11);
	cashRegisterCV[2] = CreateCV("registerCV2", 11);
	cashRegisterCV[3] = CreateCV("registerCV3", 11);
	cashRegisterCV[4] = CreateCV("registerCV4", 11);
	
	cashRegisterAssistanceLock = CreateLock("cashRegisterAssistanceLock", charCount("cashRegisterAssistanceLock\n")-1); 
	
	cashRegisterAssistanceCV[0] = CreateCV("cashRegister0AssistanceCV", charCount("cashRegister_AssistanceCV\n")-1);
	cashRegisterAssistanceCV[1] = CreateCV("cashRegister1AssistanceCV", charCount("cashRegister_AssistanceCV\n")-1);
	cashRegisterAssistanceCV[2] = CreateCV("cashRegister2AssistanceCV", charCount("cashRegister_AssistanceCV\n")-1);
	cashRegisterAssistanceCV[3] = CreateCV("cashRegister3AssistanceCV", charCount("cashRegister_AssistanceCV\n")-1);
	cashRegisterAssistanceCV[4] = CreateCV("cashRegister4AssistanceCV", charCount("cashRegister_AssistanceCV\n")-1);

	
	/*Write("CASH REGISTERS DATA done.\n", charCount("CASH REGISTERS DATA done.\n"), ConsoleOutput);*/
	
	shelfLocks[0] = CreateLock("shelfLock0", 10);
	shelfLocks[1] = CreateLock("shelfLock1", 10);
	shelfLocks[2] = CreateLock("shelfLock2", 10);
	shelfLocks[3] = CreateLock("shelfLock3", 10);
	shelfLocks[4] = CreateLock("shelfLock4", 10);
	shelfLocks[5] = CreateLock("shelfLock5", 10);
	shelfLocks[6] = CreateLock("shelfLock6", 10);
	shelfLocks[7] = CreateLock("shelfLock7", 10);
	shelfLocks[8] = CreateLock("shelfLock8", 10);
	shelfLocks[9] = CreateLock("shelfLock9", 10);
	
	/*Write("SHELF LOCKS done.\n", charCount("SHELF LOCKS done.\n"), ConsoleOutput);*/

	stockboyStatusLock = CreateLock("stockboyStatusLock", charCount("stockboyStatusLock\n")-1);
	stockroomLock = CreateLock("stockroomLock", charCount("stockroomLock\n")-1);
	stockroomLineLock = CreateLock("stockroomLineLock", charCount("stockroomLineLock\n")-1);
	stockroomLineCV = CreateCV("stockroomCV", charCount("stockroomCV")-1);
	
	customerWithStockboyLock[0] = CreateLock("customerWithStockboy0Lock", charCount("customerWithStockboy0Lock\n")-1);
	customerWithStockboyLock[1] = CreateLock("customerWithStockboy1Lock", charCount("customerWithStockboy1Lock\n")-1);
	customerWithStockboyLock[2] = CreateLock("customerWithStockboy2Lock", charCount("customerWithStockboy2Lock\n")-1);
	customerWithStockboyLock[3] = CreateLock("customerWithStockboy3Lock", charCount("customerWithStockboy3Lock\n")-1);
	customerWithStockboyLock[4] = CreateLock("customerWithStockboy4Lock", charCount("customerWithStockboy4Lock\n")-1);
	customerWithStockboyLock[5] = CreateLock("customerWithStockboy5Lock", charCount("customerWithStockboy5Lock\n")-1);
	customerWithStockboyLock[6] = CreateLock("customerWithStockboy6Lock", charCount("customerWithStockboy6Lock\n")-1);
	customerWithStockboyLock[7] = CreateLock("customerWithStockboy7Lock", charCount("customerWithStockboy7Lock\n")-1);
	customerWithStockboyLock[8] = CreateLock("customerWithStockboy8Lock", charCount("customerWithStockboy8Lock\n")-1);
	customerWithStockboyLock[9] = CreateLock("customerWithStockboy9Lock", charCount("customerWithStockboy9Lock\n")-1);

	stockboyLocks[0] = CreateLock("stockboy0lock", charCount("stockboy0lock\n")-1);
	stockboyLocks[1] = CreateLock("stockboy1lock", charCount("stockboy1lock\n")-1);
	stockboyLocks[2] = CreateLock("stockboy2lock", charCount("stockboy2lock\n")-1);
	stockboyLocks[3] = CreateLock("stockboy3lock", charCount("stockboy3lock\n")-1);
	stockboyLocks[4] = CreateLock("stockboy4lock", charCount("stockboy4lock\n")-1);
	stockboyLocks[5] = CreateLock("stockboy5lock", charCount("stockboy5lock\n")-1);
	stockboyLocks[6] = CreateLock("stockboy6lock", charCount("stockboy6lock\n")-1);
	stockboyLocks[7] = CreateLock("stockboy7lock", charCount("stockboy7lock\n")-1);
	stockboyLocks[8] = CreateLock("stockboy8lock", charCount("stockboy8lock\n")-1);
	stockboyLocks[9] = CreateLock("stockboy9lock", charCount("stockboy9lock\n")-1);
	
	stockboysCV[0] = CreateCV("stockboy0CV", charCount("stockboy0CV\n")-1);
	stockboysCV[1] = CreateCV("stockboy1CV", charCount("stockboy1CV\n")-1);
	stockboysCV[2] = CreateCV("stockboy2CV", charCount("stockboy2CV\n")-1);
	stockboysCV[3] = CreateCV("stockboy3CV", charCount("stockboy3CV\n")-1);
	stockboysCV[4] = CreateCV("stockboy4CV", charCount("stockboy4CV\n")-1);
	stockboysCV[5] = CreateCV("stockboy5CV", charCount("stockboy5CV\n")-1);
	stockboysCV[6] = CreateCV("stockboy6CV", charCount("stockboy6CV\n")-1);
	stockboysCV[7] = CreateCV("stockboy7CV", charCount("stockboy7CV\n")-1);
	stockboysCV[8] = CreateCV("stockboy8CV", charCount("stockboy8CV\n")-1);
	stockboysCV[9] = CreateCV("stockboy9CV", charCount("stockboy9CV\n")-1);
	
	/*Write("STOCKBOY DATA done.\n", charCount("STOCKBOY DATA done.\n"), ConsoleOutput);*/
	
	aisleHelpLocks[0] = CreateLock("aisle0Lock", 10);
	aisleHelpLocks[1] = CreateLock("aisle1Lock", 10);
	aisleHelpLocks[2] = CreateLock("aisle2Lock", 10);
	aisleHelpLocks[3] = CreateLock("aisle3Lock", 10);
	aisleHelpLocks[4] = CreateLock("aisle4Lock", 10);
	
	aisleCV[0] = CreateCV("aisle0CV", 8);
	aisleCV[1] = CreateCV("aisle1CV", 8);
	aisleCV[2] = CreateCV("aisle2CV", 8);
	aisleCV[3] = CreateCV("aisle3CV", 8);
	aisleCV[4] = CreateCV("aisle4CV", 8);
	
	stockboyReadyLock[0] = CreateLock("stockboyReady0Lock", charCount("stockboyReady0Lock\n")-1);
	stockboyReadyLock[1] = CreateLock("stockboyReady1Lock", charCount("stockboyReady1Lock\n")-1);
	stockboyReadyLock[2] = CreateLock("stockboyReady2Lock", charCount("stockboyReady2Lock\n")-1);
	stockboyReadyLock[3] = CreateLock("stockboyReady3Lock", charCount("stockboyReady3Lock\n")-1);
	stockboyReadyLock[4] = CreateLock("stockboyReady4Lock", charCount("stockboyReady4Lock\n")-1);
	
	/*Write("AISLE DATA done.\n", charCount("AISLE DATA done.\n"), ConsoleOutput);*/
	
	customerAssistanceButtonLock = CreateLock("customerAssistanceButtonLock", charCount("customerAssistanceButtonLock\n")-1);
	/*Write("CABL done.\n", charCount("CABL done.\n"), ConsoleOutput);*/
	customerAssistanceButton = FALSE;
	/*Write("CAB done.\n", charCount("CAB done.\n"), ConsoleOutput);*/
	customerAssistanceButtonCV = CreateCV("customerAssistanceButtonCV", charCount("customerAssistanceButtonLock\n")-1);
	/*Write("CABCV done.\n", charCount("CABCV done.\n"), ConsoleOutput);*/
	
	checkoutAssistanceLock = CreateLock("checkoutAssistanceLock", charCount("checkoutAssistanceLock\n")-1);
	checkoutAssistance = FALSE;
	checkoutAssistanceCV = CreateCV("checkoutAssistanceCV", charCount("checkoutAssistanceCV\n")-1);

	customerAtCheckoutLocks[0] = CreateLock("customerAtCheckoutLock0\n", charCount("customerAtCheckoutLock0\n")-1);
	customerAtCheckoutLocks[1] = CreateLock("customerAtCheckoutLock1\n", charCount("customerAtCheckoutLock1\n")-1);
	customerAtCheckoutLocks[2] = CreateLock("customerAtCheckoutLock2\n", charCount("customerAtCheckoutLock2\n")-1);
	customerAtCheckoutLocks[3] = CreateLock("customerAtCheckoutLock3\n", charCount("customerAtCheckoutLock3\n")-1);
	customerAtCheckoutLocks[4] = CreateLock("customerAtCheckoutLock4\n", charCount("customerAtCheckoutLock4\n")-1);
	
	customerAtCheckoutCV[0] = CreateCV("customerAtCheckoutLock0\n", charCount("customerAtCheckoutLock0\n")-1);
	customerAtCheckoutCV[1] = CreateCV("customerAtCheckoutLock1\n", charCount("customerAtCheckoutLock1\n")-1);
	customerAtCheckoutCV[2] = CreateCV("customerAtCheckoutLock2\n", charCount("customerAtCheckoutLock2\n")-1);
	customerAtCheckoutCV[3] = CreateCV("customerAtCheckoutLock3\n", charCount("customerAtCheckoutLock3\n")-1);
	customerAtCheckoutCV[4] = CreateCV("customerAtCheckoutLock4\n", charCount("customerAtCheckoutLock4\n")-1);
}

int Customer_findShortestLine(int initialCall) {
	int k;
	int shortestLineIndex = -1;
	int shortestLineLength = 999;
	/* find shortest line */
	LockAcquire(clerkLineLock);
	
	for(k = 0 ; k < MAXCLERKS ; k++)
	{
		/* clerkState of 1 means on break, 0 means currently working */
		if(clerkState[k] == 1)
		{
			continue;
		}
		if(clerkLineLength[k] < shortestLineLength)
		{
			shortestLineLength = clerkLineLength[k];
			shortestLineIndex = k;
		}
	}

	/* if all are clerks on break */
	if(shortestLineIndex == -1)
	{
		
		LockRelease(clerkLineLock);
		LockAcquire(checkoutAssistanceLock);
		
		Write("All clerks on break.\n", charCount("All clerks on break.\n"), ConsoleOutput);
		if(checkoutAssistance == FALSE && initialCall == 1)
		{
			checkoutAssistance = TRUE;
			CVWait(checkoutAssistanceCV, checkoutAssistanceLock);
			Yield();
		}
		else 
		{
			CVWait(checkoutAssistanceCV, checkoutAssistanceLock);
			Yield();
		}
		LockRelease(checkoutAssistanceLock);
		shortestLineIndex = Customer_findShortestLine(0);
	}
	return shortestLineIndex;
}

int Customer_findStockboy(int id, int initialCall) {
	int i, j;
	int stockboyAisle;
	
	LockAcquire(stockboyStatusLock); 
	stockboyAisle = -1; /* reset our stockboy aisle variable */
	for( i = 0 ; i < MAXSTOCKBOYS ; i++) /* search all stockboys */
	{
		for(j = 0; j < MAXAISLES ; j++) /* and see their current location */
		{
			if(stockboyStatus[i] == j) /* if they are currently in an aisle */
			{
				stockboyAisle = j; /* save it in our stockboy aisle variable */
				SWrite("Customer [%d] is looking for assistance in Aisle [%d].\n",
						charCount("Customer [%d] is looking for assistance in Aisle [%d].\n"), id, stockboyAisle);
				break; /* break out of inner for loop */
			}
		}
		if(stockboyAisle != -1) /* if a stockboy is already found then stop searching */
		{
			break; /* get out of outer for loop */
		}
	}
	
	/* if a stockboy can't be found in any aisle, press the customerAssistanceButton */
	if(stockboyAisle == -1) { 
		SWrite("Customer [%d] can't find a stockboy at all.\n",
			charCount("Customer [%d] can't find a stockboy at all.\n"), id, 0);
	
		LockAcquire(customerAssistanceButtonLock);
		LockRelease(stockboyStatusLock);

		if(customerAssistanceButton == FALSE && initialCall == 1) {
			/* Press Assistance Button */
			customerAssistanceButton = TRUE;
			CVWait(customerAssistanceButtonCV, customerAssistanceButtonLock);
		}
		else {
			/*some customer already pressed it */
			CVWait(customerAssistanceButtonCV, customerAssistanceButtonLock);
		}
		
		LockRelease(customerAssistanceButtonLock);
		stockboyAisle = Customer_findStockboy(id, 0);
	}
	return stockboyAisle;
}

/* MUST ACQUIRE stockboyStatusLock AND aisleHelpLock before this method */
/* establishes the connection between a customer and a stockboy that's ready to help */
int Customer_approachStockboy(int id, int myAisle, int itemNeeded) {
	int i;
	int myStockboy = -1;
	
	LockAcquire(stockboyStatusLock);
	/* search array for a stockboy that's available (he is stockboyHelpAisle + 10) */
	for(i = 0 ; i < MAXSTOCKBOYS ; i++) {
		if(stockboyStatus[i] == myAisle + 10) {
			myStockboy = i;
			break;
		}
	}

	if(myStockboy == -1) {
		SWrite("FATAL ERROR: Customer[%d] called out of aisle[%d] help line but cannot find an available StockBoy.\n",
			charCount("FATAL ERROR: Customer[%d] called out of aisle[%d] help line but cannot find an available StockBoy.\n"), id, myAisle);
		Halt();
	}
	
	/* accept this stockboy's help by giving him a normal status */
	stockboyStatus[myStockboy] -= 10;
	LockRelease(stockboyStatusLock);
	/* set data to be shared with the stockboy */
	LockAcquire(customerWithStockboyLock[myStockboy]);
	customerWithStockboy[myStockboy] = id;
	stockboyProductToHelp[myStockboy] = itemNeeded;
	LockRelease(customerWithStockboyLock[myStockboy]);
	return  myStockboy;
}

int Customer_askStockboy(id, myStockboy) {
	int i;
	int sizes[2];
	int inputs[3];
	int shelfNumber = -1;
	int aisleNumber = -1;
	LockAcquire(stockboyLocks[myStockboy]);
	
	SWrite("Customer [%d] is asking for assistance from StockBoy [%d].\n", 
		charCount("Customer [%d] is asking for assistance from StockBoy [%d].\n"), id, myStockboy);
	CVSignal(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	CVWait(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	
	LockAcquire(customerWithStockboyLock[myStockboy]);
	shelfNumber = shelfNumberForCustomer[myStockboy];
	aisleNumber = aisleNumberForCustomer[myStockboy];
	LockRelease(customerWithStockboyLock[myStockboy]);
	
	sizes[0] = charCount("Customer [%d] got aisle [%d] from StockBoy [%d].\n");
	sizes[1] = 3;
	
	inputs[0] = id;
	inputs[1] = aisleNumber;
	inputs[2] = myStockboy;
	
	WriteAll("Customer [%d] got aisle [%d] from StockBoy [%d].\n", sizes, inputs);
	CVSignal(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	
	LockRelease(stockboyLocks[myStockboy]);

	return shelfNumber;
}

void Customer_cantFindItem(int id, int item, int *cart, int cartIndex) {
	int i, j, k;
	int helpAisle = -1;
	int myStockboy = -1;
	int shelfToFindItem = -1;
	
	SWrite("Customer [%d] is not able to find item [%d] and is searching for StockBoy.\n",
		charCount("Customer [%s] is not able to find item [%d] and is searching for StockBoy.\n"), 
		id, item);
		
	helpAisle = Customer_findStockboy(id, 1);
	
	/* - we have found a stockboy
	*  - we still have the stockboyStatusLock
	*  - we know our stockboy's aisle number */
	LockRelease(stockboyStatusLock);
	LockAcquire(aisleHelpLocks[helpAisle]);
	
	/* get in the help line of the aisle that we found a stockboy in */
	aisleHelpLine[helpAisle]++;
	SWrite("Customer [%d] Lining up for help in aisle [%d].\n", 
		charCount("Customer [%d] Lining up for help in aisle [%d].\n"), id, helpAisle);
	CVWait(aisleCV[helpAisle], aisleHelpLocks[helpAisle]);

	/* get out of help line */
	aisleHelpLine[helpAisle]--;
	/*SWrite("Customer [%d] got out of Help Line [%d].\n", 
		charCount("Customer [%d] got out of Help Line [%d].\n"), id, helpAisle);*/
	
	
	/* secure the attention of a stockboy */
	myStockboy = Customer_approachStockboy(id, helpAisle, item);
	/*SWrite("Customer [%d] sees StockBoy [%d].\n", 
		charCount("Customer [%d] sees StockBoy [%d].\n"), id, myStockboy);*/
	LockAcquire(stockboyLocks[myStockboy]);
	
	CVSignal(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	LockRelease(aisleHelpLocks[helpAisle]);
	CVWait(stockboysCV[myStockboy], stockboyLocks[myStockboy]);
	
	
	shelfToFindItem = Customer_askStockboy(id, myStockboy);

	if(shelfToFindItem == -1) {
		SWrite("Customer [%d] recieved invalid shelf [%d].\n",
			charCount("Customer [%d] recieved invalid shelf [%d].\n"),
			id, shelfToFindItem);
		Customer_cantFindItem(id, item, (int *)cart, cartIndex);
		return;
	}	
	
	/* customer "looking" for the item */
	/* also allows time in case that particular shelf is empty and is being restocked */
	for(i=0; i<20; i++) {
		Yield();
	}
									
	/* customer gets item from the shelf */
	LockAcquire(shelfLocks[shelfToFindItem]);
	/* take an item and put it in the cart */
	if(shelves[shelfToFindItem].currentItemCount > 0) {
		shelves[shelfToFindItem].currentItemCount--;
		cart[cartIndex] = shelves[shelfToFindItem].shelfItemType;
		SWrite("Customer [%d] added item [%d] to shopping cart.\n",
			charCount("Customer [%d] added item [%d] to shopping cart.\n"),
			id, cart[cartIndex]);
		LockRelease(shelfLocks[shelfToFindItem]);
	}
	else {
		/* shelf is empty */
		SWrite("Customer [%d] sees shelf [%d] is empty.\n",
			charCount("Customer [%d] sees shelf [%d] is empty.\n"),
			id, shelfToFindItem);
		/* Stock Boy Assistnace Again */
		LockRelease(shelfLocks[shelfToFindItem]);
		LockAcquire(customerAssistanceButtonLock);

		if(customerAssistanceButton == FALSE) {
			/* Press Assistance Button */
			customerAssistanceButton = TRUE;
			CVWait(customerAssistanceButtonCV, customerAssistanceButtonLock);
		}
		else {
			/*some customer already pressed it */
			CVWait(customerAssistanceButtonCV, customerAssistanceButtonLock);
		}
		
		LockRelease(customerAssistanceButtonLock);
		LockAcquire(shelfLocks[shelfToFindItem]);
		while(shelves[shelfToFindItem].currentItemCount == 0) {
			LockRelease(shelfLocks[shelfToFindItem]);
			for(i=0; i<10; i++) {
				Yield();
			}
			LockAcquire(shelfLocks[shelfToFindItem]);
		}
		shelves[shelfToFindItem].currentItemCount--;
		cart[cartIndex] = shelves[shelfToFindItem].shelfItemType;
		SWrite("Customer [%d] added item [%d] to shopping cart.\n",
			charCount("Customer [%d] added item [%d] to shopping cart.\n"),
			id, cart[cartIndex]);
		LockRelease(shelfLocks[shelfToFindItem]);
	}
}

void Customer() {
	
	int i, j, k,m;
	int randomness;
	boolean returning;
	int id;
	int shortestLineIndex = 0;
	int shortestLineLength = 0;
	int shoppingCart[MAXCARTSIZE];
	int shoppingCartSize = 0;
	int shoppingList[MAXCARTSIZE];
	int shoppingListSize = 0;
	int stockboyHelpAisle = -1;
	int myStockboy = -1;
	int itemTypeNeeded = -1;
	int aisleToGoTo = -1;
	int sizes[2];
	int inputs[3];
	
	Write("Starting Customer\n", 18, ConsoleOutput);
	LockAcquire(globalLock);
	id = customerCount;
	customerCount++;
	/*SWrite("[%d] customers in store.\n", 25, customerCount, 0);*/
	LockRelease(globalLock);
	
	sizes[1] = 3;
	inputs[0] = id;
	
	/* 25% of the time, the Customer will be returning items instead of shopping */
	k = Rand(0,4);
	if(k == 0) {
		returning = TRUE;
	} else {
		returning = FALSE;
	}
	
	if(returning == FALSE) {
		/*  Fill Shopping list */
		/* Randomly generate an int from 1 to 10 */
		shoppingListSize = Rand(1,10);
		/* For each item, randomly generate a productID */
		for(k = 0 ; k < shoppingListSize ; k++) {
			/* Randomly generate an int from 0 to 9 */
			shoppingList[k] = shelves[Rand(0,(int)MAXSHELVES-1)].shelfItemType;
		}
		SWrite("Customer [%d] wants to buy [%d] no.of items.\n", charCount("Customer [%d] wants to buy [%d] no.of items.\n"),
			id, shoppingListSize);

		/* Go find the items on the shopping list */
		for(k = 0 ; k < shoppingListSize ; k++)
		{	
			SWrite("Customer [%d] looking for item [%d].\n",
				charCount("Customer [%d] looking for item [%d].\n"), id, shoppingList[k]);
				
			randomness = Rand(1,4);
			if(randomness != 0 )
			{
				/* 75% of the time, the customer CAN find what he is looking for*/
			
				LockAcquire(shelfLocks[shoppingList[k]]);
				/* take an item and put it in my cart */
				if(shelves[shoppingList[k]].currentItemCount > 0)
				{
					shelves[shoppingList[k]].currentItemCount--;
					shoppingCart[k] = shelves[shoppingList[k]].shelfItemType;
					SWrite("Customer [%d] added item [%d] to shopping cart.\n",
							charCount("Customer [%d] added item [%d] to shopping cart.\n"),
							id, shoppingCart[k]);
					LockRelease(shelfLocks[shoppingList[k]]);
				}
				else
				{
					/* shelf is empty */
					/* Stock Boy Assistnace */
					itemTypeNeeded = shelves[shoppingList[k]].shelfItemType; /* save which item we need to ask the stockboy about*/
					LockRelease(shelfLocks[shoppingList[k]]);
					
					Customer_cantFindItem(id, itemTypeNeeded, shoppingCart, k);
				}
			}
			else /* cant-find-an-item scenario */
			{
				LockAcquire(shelfLocks[shoppingList[k]]);
				itemTypeNeeded = shelves[shoppingList[k]].shelfItemType; /* save which item we need to ask the stockboy about*/
				LockRelease(shelfLocks[shoppingList[k]]);
				
				Customer_cantFindItem(id, itemTypeNeeded, shoppingCart, k);
			}/*end for loop for finding items*/
			shoppingCartSize++;
		} /* end for */
	}
	else {
	/* Customer is a returning customer */
		/* Randomly Fill Shopping Cart */
		shoppingCartSize = Rand(1,10);
		
		for(k = 0 ; k < shoppingCartSize ; k++)
		{
			/* Randomly generate an int from 0 to 9 */
			shoppingCart[k] = Rand(0,(int)MAXSHELVES-1);
		}
		SWrite("Customer [%d] needs to return [%d] no.of items.\n",
			charCount("Customer [%d] needs to return [%d] no.of items.\n"), id, shoppingCartSize);
	}
	
	shortestLineIndex = Customer_findShortestLine(1);
	
	clerkLineLength[shortestLineIndex]++;
	
	sizes[0] = charCount("Customer [%d] chose Clerk [%d] with line length of [%d].\n");
	inputs[1] = shortestLineIndex;
	inputs[2] = clerkLineLength[shortestLineIndex]-1;
	WriteAll("Customer [%d] chose Clerk [%d] with line length of [%d].\n", sizes, inputs);

	CVWait(clerkLineCV[shortestLineIndex], clerkLineLock);
	clerkLineLength[shortestLineIndex]--;
	LockAcquire(clerkLocks[shortestLineIndex]);
	LockRelease(clerkLineLock);
	
	/*SWrite("Customer [%d] approaching clerk[%d].\n", charCount("Customer [%d] approaching clerk[%d].\n"),
			id, shortestLineIndex);*/
	
	refundingTransaction[shortestLineIndex] = returning;
	customerAtCheckout[shortestLineIndex] = id;
	CVSignal(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
	CVWait(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);

	for(k = 0 ; k < shoppingCartSize ; k++)
	{
		productToScan[shortestLineIndex] = shoppingCart[k];

		sizes[0] = charCount("Customer [%d] gives item [%d] to Clerk [%d].\n");
		inputs[1] = productToScan[shortestLineIndex];
		inputs[2] = shortestLineIndex;
		/*WriteAll("Customer [%d] gives item [%d] to Clerk [%d].\n", sizes, inputs);*/
				
		if(k == shoppingCartSize - 1) 
		{
			productScanningFinished[shortestLineIndex] = TRUE;
			sizes[0] = charCount("Customer [%d] tells Clerk [%d], I have given you all my [%d] items.\n");
			inputs[1] = shortestLineIndex;
			inputs[2] = shoppingCartSize;
			WriteAll("Customer [%d] tells Clerk [%d], I have given you all my [%d] items.\n", sizes, inputs);
			
		}
		CVSignal(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
		CVWait(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
	}

	productScanningFinished[shortestLineIndex] = FALSE;
	
	if(returning == FALSE) {
		amountPaid[shortestLineIndex] = paymentDue[shortestLineIndex];
		
		sizes[0] = charCount("Customer [%d] pays [%d] to Clerk [%d] and is now waiting for receipt.\n");
		inputs[1] = amountPaid[shortestLineIndex];
		inputs[2] = shortestLineIndex;
		WriteAll("Customer [%d] pays [%d] to Clerk [%d] and is now waiting for receipt.\n", sizes, inputs);
	}
	else {
		sizes[0] = charCount("Customer [%d] receives [%d] from Clerk [%d] and is now waiting for receipt.\n");
		inputs[1] = paymentDue[shortestLineIndex];
		inputs[2] = shortestLineIndex;
		WriteAll("Customer [%d] receives [%d] from Clerk [%d] and is now waiting for receipt.\n", sizes, inputs);
	}
	
	CVSignal(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
	CVWait(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
		
	SWrite("Customer [%d] got receipt from Clerk [%d] and is now leaving.\n", 
		charCount("Customer [%d] got receipt from Clerk [%d] and is now leaving.\n"), id, shortestLineIndex);
	CVSignal(clerkCV[shortestLineIndex], clerkLocks[shortestLineIndex]);
	LockRelease(clerkLocks[shortestLineIndex]);
	
	Exit(-1);
}

void Clerk() {
	int k;
	int clerkId;
	int paymentNeededFromCustomer;
	int paymentReceived;
	int sizes[2];
	int inputs[3];
	
	Write("Starting Clerk\n", 15, ConsoleOutput);
	LockAcquire(globalLock);
	clerkId = clerkCount;
	clerkCount++;
	SWrite("[%d] clerks in store.\n", 22, clerkCount, 0);
	LockRelease(globalLock);
	
	sizes[1] = 3;
	inputs[0] = clerkId;
	
	/* go directly to work */
	if(clerkId == 0) {
	LockAcquire(clerkLineLock);
	clerkState[clerkId] = 0;
	LockRelease(clerkLineLock);
	}
	
	while(1)
	{
		LockAcquire(clerkLineLock);
		/*SWrite("Clerk [%d] looking at the line length [%d].\n", charCount("Clerk [%d] looking at the line.\n"), clerkId, clerkLineLength[clerkId]);*/
		if(clerkLineLength[clerkId] > 0 && clerkState[clerkId]==0)
		{
			if(clerkLineLength[clerkId] > 4)
			{
				LockAcquire(clerkAssistanceButtonLock);
				if(clerkAssistanceButton == FALSE)
				{
					clerkAssistanceButton = TRUE;
					SWrite("Clerk [%d] sees more than 4 people in line and calls Manager.\n",
						charCount("Clerk [%d] sees more than 4 people in line and calls Manager.\n"), clerkId, 0);
				}
				LockRelease(clerkAssistanceButtonLock);
			}
			/*SWrite("Clerk [%d]: I have a customer in line.\n", charCount("Clerk [%d]: I have a customer in line.\n"),
				clerkId, 0);*/
			LockAcquire(clerkLocks[clerkId]);
			CVSignal(clerkLineCV[clerkId], clerkLineLock);
			LockRelease(clerkLineLock);
			CVWait(clerkCV[clerkId], clerkLocks[clerkId]);
			
			SWrite("Clerk [%d] tells Customer[%d] to place items on counter.\n",
				charCount("Clerk [%d] tells Customer[%d] to place items on counter.\n"), clerkId, customerAtCheckout[clerkId]);
			
			CVSignal(clerkCV[clerkId], clerkLocks[clerkId]);
			CVWait(clerkCV[clerkId], clerkLocks[clerkId]);
			
			LockAcquire(cashRegisterLocks[clerkId]);
			while(1)
			{
				sizes[0] = charCount("Clerk [%d] got item [%d] from Customer[%d].\n");
				inputs[1] = productToScan[clerkId];
				inputs[2] = customerAtCheckout[clerkId];
				/*WriteAll("Clerk [%d] got item [%d] from Customer[%d].\n", sizes, inputs);*/
		
				sizes[0] = charCount("Clerk [%d] gave item [%d] to Cash Register[%d] for scanning.\n");
				inputs[2] = clerkId;
				/*WriteAll("Clerk [%d] gave item [%d] to Cash Register[%d] for scanning.\n", sizes, inputs);*/
				
				if(productScanningFinished[clerkId] == TRUE)
				{
					cashRegisterFinishedStatus[clerkId] = TRUE;
					/* give last item to cash register */
					CVSignal(cashRegisterCV[clerkId], cashRegisterLocks[clerkId]);
					CVWait(cashRegisterCV[clerkId], cashRegisterLocks[clerkId]);
					break;
				}
				
				CVSignal(cashRegisterCV[clerkId], cashRegisterLocks[clerkId]);
				CVWait(cashRegisterCV[clerkId], cashRegisterLocks[clerkId]);
				/* cash register confirmed item, wait for customer to give next item and wait */

				CVSignal(clerkCV[clerkId], clerkLocks[clerkId]);
				CVWait(clerkCV[clerkId], clerkLocks[clerkId]);
			}
			
			/* tell cash register to give me total */
			CVSignal(cashRegisterCV[clerkId], cashRegisterLocks[clerkId]);
			CVWait(cashRegisterCV[clerkId], cashRegisterLocks[clerkId]);
			
			if(refundingTransaction[clerkId] == FALSE) {
				sizes[0] = charCount("Clerk [%d] got total cost $[%d] from Cash Register [%d].\n");
				inputs[1] = paymentDue[clerkId];
				inputs[2] = clerkId;
				/*WriteAll("Clerk [%d] got total cost $[%d] from Cash Register [%d].\n", sizes, inputs);*/
					
				sizes[0] = charCount("Clerk [%d] tells Customer[%d] total cost is $[%d].\n");
				inputs[1] = customerAtCheckout[clerkId];
				inputs[2] = paymentDue[clerkId];
				/*WriteAll("Clerk [%d] tells Customer[%d] total cost is $[%d].\n", sizes, inputs);*/
			
				CVSignal(clerkCV[clerkId], clerkLocks[clerkId]);
				CVWait(clerkCV[clerkId], clerkLocks[clerkId]);
			
				sizes[0] = charCount("Clerk [%d] got money $[%d] from Customer [%d].\n");
				inputs[1] = amountPaid[clerkId];
				inputs[2] = customerAtCheckout[clerkId];
				/*WriteAll("Clerk [%d] got money $[%d] from Customer [%d].\n", sizes, inputs);*/
					
				sizes[0] = charCount("Clerk [%d] gave the money $[%d] to Cash Register [%d].\n");
				inputs[1] = amountPaid[clerkId];
				inputs[2] = clerkId;
				/*WriteAll("Clerk [%d] gave the money $[%d] to Cash Register [%d].\n", sizes, inputs);*/
			}
			else {
				sizes[0] = charCount("Clerk [%d] got money $[%d] from Cash Register [%d].\n");
				inputs[1] = paymentDue[clerkId];
				inputs[2] = clerkId;
				/*WriteAll("Clerk [%d] got money $[%d] from Cash Register [%d].\n", sizes, inputs);*/

				sizes[0] = charCount("Clerk [%d] gave the money $[%d] to Customer[%d].\n");
				inputs[1] = paymentDue[clerkId];
				inputs[2] = customerAtCheckout[clerkId];
				/*WriteAll("Clerk [%d] gave the money $[%d] to Customer[%d].\n", sizes, inputs);*/
					
				CVSignal(clerkCV[clerkId], clerkLocks[clerkId]);
				CVWait(clerkCV[clerkId], clerkLocks[clerkId]);
			}
			
			/* tell Cash Register to print receipt */
			CVSignal(cashRegisterCV[clerkId], cashRegisterLocks[clerkId]);
			CVWait(cashRegisterCV[clerkId], cashRegisterLocks[clerkId]);
	
			/*SWrite("Clerk [%d] got receipt from Cash Register [%d].\n", 
				charCount("Clerk [%d] got receipt from Cash Register [%d].\n"), clerkId, clerkId );
			SWrite("Clerk [%d] gave the receipt to Customer [%d] and tells him to leave.\n", 
				charCount("Clerk [%d] gave the receipt to Customer [%d] and tells him to leave.\n"),
				clerkId, customerAtCheckout[clerkId]);*/

			CVSignal(clerkCV[clerkId], clerkLocks[clerkId]);
			CVWait(clerkCV[clerkId], clerkLocks[clerkId]);
			LockRelease(clerkLocks[clerkId]);
			/* Customer is gone. Check line for new customers */
			LockAcquire(globalLock);
			totalCustomersServed++;
			LockRelease(globalLock);
		}
		else 
		{
			clerkState[clerkId] = 1;
			/*SWrite("clerkLocks[%d] = [%d]\n", charCount("clerkLocks[%d] = [%d]\n"), clerkId, clerkLocks[clerkId]);*/
			LockAcquire(clerkLocks[clerkId]);
			LockRelease(clerkLineLock);
			SWrite("Clerk [%d] is leaving Cash Register [%d] and going on break.\n",
				charCount("Clerk [%d] is leaving Cash Register [%d] and going on break.\n"), clerkId, clerkId);
			CVWait(clerkCV[clerkId], clerkLocks[clerkId]);
			SWrite("Clerk [%d] was called by Manager to work.\n",
				charCount("Clerk [%d] was called by Manager to work.\n"), clerkId, 0);
			LockAcquire(clerkLineLock);
			/*SWrite("clerkLocks[%d] = [%d]\n", charCount("clerkLocks[%d] = [%d]\n"), clerkId, clerkLocks[clerkId]);*/
			LockRelease(clerkLocks[clerkId]);
			clerkState[clerkId] = 0;
			LockRelease(clerkLineLock);
			
			for(k = 0 ; k < 30 ; k++) {
				/*SWrite("Clerk [%d] yielding.\n", charCount("Clerk [%d] yielding.\n"), clerkId, 0);*/
				Yield();
			}
		}
	} /* end while */
	Exit(-1);
}

void CashRegister_checkScanner(int id)
{
	int sizes[2];
	int inputs[3];
	int currentAmountDue = 0;
	int totalProducts = 0;
	
	while(1)
	{	
		currentAmountDue += 1;
		totalProducts++;
		/*SWrite("Cash Register [%d] scanned item [%d].\n", charCount("Cash Register [%d] scanned item [%d].\n"),
			id, productToScan[id]);*/
		if(cashRegisterFinishedStatus[id] == TRUE)
		{
			paymentDue[id] = currentAmountDue;
			cashRegisterFinishedStatus[id] = FALSE;
			sizes[0] = charCount("Cash Register [%d] scanned all [%d] items for Customer [%d].\n");
			sizes[1] = 3;
			inputs[0] = id;
			inputs[1] = totalProducts;
			inputs[2] = customerAtCheckout[id];
			WriteAll("Cash Register [%d] scanned all [%d] items for Customer [%d].\n", sizes, inputs);
			CVSignal(cashRegisterCV[id], cashRegisterLocks[id]);
			CVWait(cashRegisterCV[id], cashRegisterLocks[id]);
			break;
		}
		CVSignal(cashRegisterCV[id], cashRegisterLocks[id]);
		CVWait(cashRegisterCV[id], cashRegisterLocks[id]);
	}
	
	
}

void CashRegister() {
	int k;
	int id;
	int totalCash = 0;
	int currentAmountDue = 0;
	int sizes[2];
	int inputs[4];
	
	Write("Starting Cash Register\n", 23, ConsoleOutput);
	LockAcquire(globalLock);
	id = cashRegisterCount;
	cashRegisterCount++;
	SWrite("[%d] cash registers in store.\n", charCount("[%d] cash registers in store.\n"), cashRegisterCount, 0);
	LockAcquire(cashRegisterLocks[id]);
	LockRelease(globalLock);
	
	sizes[1] = 4;
	inputs[0] = id;
	
	while(1) {
	
		CVWait(cashRegisterCV[id], cashRegisterLocks[id]);
		
		CashRegister_checkScanner(id);
		/* cash register has scanned everything */
		
		sizes[0] = charCount("Cash Register [%d] gave total cost $[%d] for Customer[%d] to Clerk[%d] for items.\n");
		inputs[1] = paymentDue[id];
		inputs[2] = customerAtCheckout[id];
		inputs[3] = id;
		/*WriteAll("Cash Register [%d] gave total cost $[%d] for Customer[%d] to Clerk[%d] for items.\n", 
			sizes, inputs);*/
		
			
		if(refundingTransaction[id] == FALSE) {
			CVSignal(cashRegisterCV[id], cashRegisterLocks[id]);
			CVWait(cashRegisterCV[id], cashRegisterLocks[id]);
			LockAcquire(globalLock);
			storeTotal += amountPaid[id];
			cashRegisterSales[id] += amountPaid[id];
			cashRegisterAmount[id] += amountPaid[id];
			LockRelease(globalLock);

			/*SWrite("Cash Register [%d] got the money from Clerk [%d].\n",
				charCount("Cash Register [%d] got the money from Clerk [%d].\n"), id, id);*/
		}
		else {
			LockAcquire(globalLock);
			/*storeTotal += amountPaid[id];*/
			
			if((cashRegisterAmount[id] - paymentDue[id]) < 0) {
				LockRelease(globalLock);
				LockAcquire(cashRegisterAssistanceLock);
				cashRegisterAssistance[id] = 1;
				cashRegisterRequest[id] = paymentDue[id];
				SWrite("Cash Register [%d] informed the manager that he needs $[%d].\n",
					charCount("Cash Register [%d] informed the manager that he needs $[%d].\n"), id, paymentDue[id]);
				CVWait(cashRegisterAssistanceCV[id], cashRegisterAssistanceLock);
				LockRelease(cashRegisterAssistanceLock);
				LockAcquire(globalLock);
			}
			cashRegisterSales[id] += 0;
			cashRegisterAmount[id] -= paymentDue[id];
			LockRelease(globalLock);
			
			/*SWrite("Cash Register [%d] gave the money to Clerk [%d].\n",
				charCount("Cash Register [%d] gave the money to Clerk [%d].\n"), id, id);*/
			CVSignal(cashRegisterCV[id], cashRegisterLocks[id]);
			CVWait(cashRegisterCV[id], cashRegisterLocks[id]);	
		}
		
		sizes[0] = charCount("Cash Register [%d] gave receipt to Clerk [%d] for Customer[%d].\n");
		sizes[1] = 3;
		inputs[1] = id;
		inputs[2] = customerAtCheckout[id];
		/*WriteAll("Cash Register [%d] gave receipt to Clerk [%d] for Customer[%d].\n", sizes, inputs);*/
		
		if(refundingTransaction[id] == FALSE) {
			/*SWrite("Cash Register [%d] total sale is $[%d].\n",
				charCount("Cash Register [%d] total sale is $[%d].\n"), id, paymentDue[id]);*/
		}
		else {
			/*SWrite("Cash Register [%d] total sale is $[%d].\n",
				charCount("Cash Register [%d] total sale is $[%d].\n"), id, (paymentDue[id]*-1));*/
		}
			
		CVSignal(cashRegisterCV[id], cashRegisterLocks[id]);
	}
	LockRelease(cashRegisterLocks[id]);
	
	Exit(-1);
}

void SB_grabItemFromStockRoom(int id, int productToRestock) {
	int i;
	LockAcquire(stockroomLineLock);
	if(stockroomLineLength > 0) {
		/* stock room is occupied and there are stockboys already waiting to go in the stock room */
		stockroomLineLength++;
		SWrite("StockBoy [%d] is waiting for StockBoy [%d] to leave the stock room.\n",
			charCount("StockBoy [%d] is waiting for StockBoy [%d] to leave the stock room.\n"), id, stockboyInStockroom);
		CVWait(stockroomLineCV, stockroomLineLock);
		stockroomLineLength--;
	}
	else {
		if(stockboyInStockroom != -1) {
			/* there is no one in line for the stock room but there is someone in the stock room */
			stockroomLineLength++;
			SWrite("StockBoy [%d] is waiting for StockBoy [%d] to leave the stock room.\n",
				charCount("StockBoy [%d] is waiting for StockBoy [%d] to leave the stock room.\n"), id, stockboyInStockroom);
			CVWait(stockroomLineCV, stockroomLineLock);
			stockroomLineLength--;
		}
	}
	/* go in the stock room */
	stockboyInStockroom = id;
	LockAcquire(stockroomLock);
	LockRelease(stockroomLineLock);
	SWrite("StockBoy [%d] is in stock room.\n", charCount("StockBoy [%d] is in stock room.\n"), id, 0);
	for(i=0; i<5; i++) {
		Yield();
	}
	SWrite("StockBoy [%d] retrieve one new item [%d] from stock room.\n",
		charCount("StockBoy [%d] retrieve one new item [%d] from stock room.\n"), id, productToRestock);
	LockAcquire(stockroomLineLock);
	SWrite("StockBoy [%d] leaves stock room.\n", charCount("StockBoy [%d] leaves stock room.\n"), id, 0);
	LockRelease(stockroomLock);
	if(stockroomLineLength == 0) {
		stockboyInStockroom = -1;
	}
	else {
		CVSignal(stockroomLineCV, stockroomLineLock);
	}
	LockRelease(stockroomLineLock);
}

/* MUST ACQUIRE AISLE HELP LOCK BEFORE CALLING THIS METHOD */
boolean SB_checkHelpLine(int aisle) {
	if(aisleHelpLine[aisle] > 0) {
		return TRUE;
	}
	return FALSE;
}

/* MUST ACQUIRE AISLE HELP LOCK BEFORE CALLING THIS METHOD */
void SB_helpCustomer(int id, int aisle) {
	int i;
	int productToLookFor;
	int *toMyAisle;
	int tempAisle;
	int myCustomer;
	int sizes[2];
	int inputs[3];
	
	LockAcquire(stockboyStatusLock);
	stockboyStatus[id] += 10;
	SWrite("StockBoy [%d] changed status.\n",
		charCount("StockBoy [%d] changed status.\n"), id, 0);
	LockRelease(stockboyStatusLock);
	LockAcquire(stockboyLocks[id]);
	SWrite("StockBoy [%d] acquired his own lock.\n",
		charCount("StockBoy [%d] acquired his own lock.\n"), id, 0);

	CVSignal(aisleCV[aisle], aisleHelpLocks[aisle]);
	/*CVWait(aisleCV[aisle], aisleHelpLocks[aisle]);*/
	LockRelease(aisleHelpLocks[aisle]);
	CVWait(stockboysCV[id], stockboyLocks[id]);
	LockRelease(stockboyReadyLock[aisle]);
	
	LockAcquire(customerWithStockboyLock[id]);
	myCustomer = customerWithStockboy[id];
	LockRelease(customerWithStockboyLock[id]);
	
	SWrite("StockBoy [%d] is approached by Customer [%d] for assistance.\n",
		charCount("StockBoy [%d] is approached by Customer [%d] for assistance.\n"), id, myCustomer);
	CVSignal(stockboysCV[id], stockboyLocks[id]);
	CVWait(stockboysCV[id], stockboyLocks[id]);
	
	LockAcquire(customerWithStockboyLock[id]);
	productToLookFor = stockboyProductToHelp[id];
	tempAisle = shelves[productToLookFor].aisleNumber;
	shelfNumberForCustomer[id] = productToLookFor;
	aisleNumberForCustomer[id] = tempAisle;
	LockRelease(customerWithStockboyLock[id]);
	
	sizes[0] = charCount("StockBoy [%d] gave aisle [%d] to Customer [%d] for item [%d].\n");
	sizes[1] = 4;
	
	inputs[0] = id;
	inputs[1] = tempAisle;
	inputs[2] = myCustomer;
	inputs[3] = productToLookFor;
	
	WriteAll("StockBoy [%d] gave aisle [%d] to Customer [%d] for item [%d].\n", sizes, inputs);
	
	CVSignal(stockboysCV[id], stockboyLocks[id]);
	CVWait(stockboysCV[id], stockboyLocks[id]);
	
	LockRelease(stockboyLocks[id]);
	
}

void StockBoy() {
	int i, j, k;
	char* name;
	int id; /* id of stockboy */
	int myAisle; /* my current aisle i am in */
	int shelfToRestock; /* the saved shelf to replenish later */
	int itemToRestock; /* the product from the stockroom */
	int tasks = 0; /* increment whenever the stockboy encounters a task (restocking or helping) */
	
	LockAcquire(globalLock);
	id = stockboyCount;
	stockboyCount++;
	SWrite("[%d] stockboys in store.\n", charCount("[%d] stockboys in store.\n"), stockboyCount, 0);
	LockRelease(globalLock);
	
	while(1) {
		LockAcquire(stockboyLocks[id]);
		while(tasks != 0 && stockboyState[id] == 0) {
			LockRelease(stockboyLocks[id]);
			tasks = 0;
			SWrite("StockBoy [%d] is checking if more items are to be re-stocked.\n",
				charCount("StockBoy [%d] is checking if more items are to be re-stocked.\n"), id, 0);
			for(i = 0 ; i < MAXAISLES ; i++) {
				/* ENTER an aisle */
				LockAcquire(stockboyStatusLock);
				myAisle = i;
				stockboyStatus[id] = myAisle;
				LockRelease(stockboyStatusLock);

				/* CHECK the shelves on that aisle*/
				for(j = (i*SHELVESPERAISLE) ; j < ((i*SHELVESPERAISLE)+SHELVESPERAISLE) ; j++) {
					shelfToRestock = -1;
					LockAcquire(shelfLocks[j]);
					
					SWrite("StockBoy [%d] is checking shelf [%d].\n",
					charCount("StockBoy [%d] is checking shelf [%d].\n"), id, j);
				
					/* CHECK a single shelf's quantity and state */
					if(shelves[j].currentItemCount != MAXSHELFQUANTITY) {
						/* no other stockboys are replenishing this shelf */
						if(shelves[j].state == NONE) {
							/* so other stockboys know this shelf is being replenished by this guy */
							shelves[j].state =  REPLENISHING;
							/* save this shelf */
							shelfToRestock = j;
							itemToRestock = shelves[j].shelfItemType;
						}
					}
					LockRelease(shelfLocks[j]);
							
					if(shelfToRestock != -1) {
						SWrite("StockBoy [%d] goes back to stock room for item [%d].\n", 
							charCount("StockBoy [%d] goes back to stock room for item [%d].\n"), id, itemToRestock);
						
						tasks++;			
						/* EXIT the aisle */
						LockAcquire(stockboyStatusLock);
						stockboyStatus[id] = -1;
						LockRelease(stockboyStatusLock);
						/* GO TO stock room */
						SB_grabItemFromStockRoom(id, itemToRestock);
						/* RE-ENTER the aisle */
						LockAcquire(stockboyStatusLock);
						stockboyStatus[id] = myAisle;
						LockRelease(stockboyStatusLock);
						/* RESTOCK the item */
						LockAcquire(stockboyStatusLock);
						stockboyStatus[id] = myAisle;
						LockRelease(stockboyStatusLock);
						LockAcquire(shelfLocks[j]);
						shelves[j].currentItemCount = shelves[j].currentItemCount + 1;
						shelves[j].state = NONE;
						/*SWrite("StockBoy [%d] putting item [%d] on shelf.\n",
							charCount("StockBoy [%d] putting item [%d] on shelf.\n"), id, itemToRestock);*/
						LockRelease(shelfLocks[j]);
						shelfToRestock = -1;
					}
				
					/* CHECK help line and help a single Customer if needed */
					LockAcquire(stockboyReadyLock[myAisle]);
					LockAcquire(aisleHelpLocks[myAisle]);
					/*SWrite("StockBoy [%d] finished checking a shelf in aisle [%d] and looking for a waiting Customer.\n",
							charCount("StockBoy [%d] finished checking a shelf in aisle [%d] and looking for a waiting Customer.\n"),
							id, myAisle);*/
					if(SB_checkHelpLine(myAisle) == TRUE) {
						tasks++;
						/*SWrite("Stockboy [%d] sees a Customer in aisle [%d] needs assistance.\n",
							charCount("Stockboy [%d] sees a Customer in aisle [%d] needs assistance.\n"), id, myAisle);*/
						SB_helpCustomer(id, myAisle);
					}
					else {
						LockRelease(aisleHelpLocks[myAisle]);
					}
				} /* end for j (end of shelf checking for this aisle) */
			
				/* MOSEY around the aisle for a bit so Customers can find the stockboy */
				for( k = 0 ; k < 10 ; k++) {
					Yield();			
				}
			
				/* HELP any waiting Customers */
	
				/* this lock keeps other stockboys from getting into this critical section until the customer 
				*  that gets signalled out of the help line has approached the first stockboy to signal on the 
				*  aisle help lock */			
				LockAcquire(stockboyReadyLock[myAisle]);
				LockAcquire(aisleHelpLocks[myAisle]);
				/*SWrite("StockBoy [%d] finished checking all shelves in aisle [%d] and looking for a waiting Customer.\n",
					charCount("StockBoy [%d] finished checking all shelves in aisle [%d] and looking for a waiting Customer.\n"),
					id, myAisle);*/
				while(SB_checkHelpLine(myAisle) == TRUE) {
					tasks++;
					/*SWrite("Stockboy [%d] sees a Customer in aisle [%d] needs assistance.\n",
							charCount("Stockboy [%d] sees a Customer in aisle [%d] needs assistance.\n"), id, myAisle);*/
					SB_helpCustomer(id, myAisle);
					LockAcquire(stockboyReadyLock[myAisle]);
					LockAcquire(aisleHelpLocks[myAisle]);
				}
				LockRelease(aisleHelpLocks[myAisle]);
				LockRelease(stockboyReadyLock[myAisle]);
			
				/* GO TO next aisle (if applicable) */
			
			} /* end for i */
			LockAcquire(stockboyLocks[id]);
		} /* end while (tasks)*/
		
		LockRelease(stockboyLocks[id]);
		/* EXIT the aisle */
		LockAcquire(stockboyStatusLock);
		stockboyStatus[id] = -1;
		SWrite("StockBoy [%d] is not in an aisle.\n", charCount("StockBoy [%d] is not in an aisle.\n"), id, 0);
		LockRelease(stockboyStatusLock);
		LockAcquire(stockboyLocks[id]);
		stockboyState[id] = 1;
		SWrite("StockBoy [%d] is going on break.\n", charCount("StockBoy [%d] is going on break.\n"), id, 0);
		CVWait(stockboysCV[id], stockboyLocks[id]);
		stockboyState[id] = 0;
		SWrite("StockBoy [%d] got call from Manager.\n", charCount("StockBoy [%d] got call from Manager.\n"), id, 0);
		LockRelease(stockboyLocks[id]);
		/* set the tasks > 0 so stockboy can get into the while loop */
		tasks = 1;
	} /* end while true */
	
	Exit(-1);
}

void Manager_monitorCustomerAssistanceButton() {
	int i, j;
	int numStockboys;
	
	LockAcquire(customerAssistanceButtonLock);
	/* if the assistance button was pressed */
	if(customerAssistanceButton == TRUE) 
	{
		/* reset the assistance button */
		customerAssistanceButton = FALSE; 
		
		LockAcquire(globalLock);
		numStockboys = stockboyCount;
		LockRelease(globalLock);
		
		for(i = 0 ; i < numStockboys ; i++)
		{
			LockAcquire(stockboyLocks[i]);
			if(stockboyState[i] == 1)
			{
				Write("Manager wakes up StockBoy.\n", charCount("Manager wakes up StockBoy.\n"), ConsoleOutput);
				CVSignal(stockboysCV[i], stockboyLocks[i]);
			}
			LockRelease(stockboyLocks[i]);
		}
		CVBroadcast(customerAssistanceButtonCV, customerAssistanceButtonLock);
	}
	LockRelease(customerAssistanceButtonLock);
}

void Manager_monitorCheckoutAssistanceButton() {
	int i;
	int numClerks;
	
	LockAcquire(checkoutAssistanceLock);
	
	if(checkoutAssistance == TRUE)
	{
		checkoutAssistance = FALSE;
		/* wake up all clerks who are on break */
		LockAcquire(globalLock);
		numClerks = clerkCount;
		LockRelease(globalLock);
		LockAcquire(clerkLineLock);
		LockRelease(checkoutAssistanceLock);
	
		for( i = 0 ; i < numClerks ; i++)
		{
			if(clerkState[i] == 1)
			{
				clerkState[i] = 0;
				CVSignal(checkoutAssistanceCV, checkoutAssistanceLock);
				LockRelease(clerkLineLock);
				LockAcquire(clerkLocks[i]);
				Write("Manager wakes up Clerk.\n", charCount("Manager wakes up Clerk.\n"), ConsoleOutput);
				CVSignal(clerkCV[i], clerkLocks[i]);
				LockAcquire(clerkLineLock);
				LockRelease(clerkLocks[i]);
			}
		}
		LockRelease(clerkLineLock);
		
	
		/* then wake up all customers waiting for the assistance */
		CVBroadcast(checkoutAssistanceCV, checkoutAssistanceLock);
		return;
	}
	LockRelease(checkoutAssistanceLock);
}

void Manager_monitorShelfInventory() {	
	int i, j;
	int numStockboys;
	
	LockAcquire(globalLock);
	numStockboys = stockboyCount;
	LockRelease(globalLock);
	
	/* first, check if there is at least one stockboy on break */
	/* if all of the stockboys are already working, there's no point in waking them up */
	for(i = 0 ; i < numStockboys ; i++) {
		LockAcquire(stockboyLocks[i]);
		
		if(stockboyState[i] == 1) {
			LockRelease(stockboyLocks[i]);
			break;
		}
		LockRelease(stockboyLocks[i]);
		if(i == numStockboys-1) {
			/*Write("Manager sees all stockboys working.\n", charCount("Manager sees all stockboys working.\n"), ConsoleOutput);*/
			return;
		}
	}

	for(i = 0 ; i < MAXSHELVES ; i++)
	{
		LockAcquire(shelfLocks[i]);
		if(shelves[i].currentItemCount == 0) {
			LockRelease(shelfLocks[i]);
			
			for( j = 0 ; j < numStockboys ; j++) {
				LockAcquire(stockboyLocks[j]);
				
				if(stockboyState[j] == 1){
					Write("Manager wakes up StockBoy.\n", charCount("Manager wakes up StockBoy.\n"), ConsoleOutput);
					CVSignal(stockboysCV[j], stockboyLocks[j]);
					LockRelease(stockboyLocks[j]);
					continue;
				}
				LockRelease(stockboyLocks[j]);
			}
			break;
		}
		LockRelease(shelfLocks[i]);
	}
}

void Manager_monitorCashRegisterAssistanceButton() {
	int i, j, k;
	int numRegisters;
	
	LockAcquire(globalLock);
	numRegisters = cashRegisterCount;
	LockRelease(globalLock);
	
	LockAcquire(cashRegisterAssistanceLock);
	for(i=0; i<numRegisters; i++) {
		if(cashRegisterAssistance[i] == 1) {
			cashRegisterAssistance[i] = 0;
			LockAcquire(globalLock);
			cashRegisterAmount[i] += cashRegisterRequest[i];
			LockRelease(globalLock);
			CVSignal(cashRegisterAssistanceCV[i], cashRegisterAssistanceLock);
		}
	}
	LockRelease(cashRegisterAssistanceLock);
}


void Manager_printTotals() {
	int i, j, k;
	int numRegisters;
	
	LockAcquire(globalLock);
	numRegisters = cashRegisterCount;

	for(i=0; i<numRegisters; i++) {
		SWrite("Total Sale from Cash Register [%d] is $[%d].\n",
			charCount("Total Sale from Cash Register [%d] is $[%d].\n"), i, cashRegisterSales[i]);
	}
	SWrite("Total Sale of the entire store is $[%d]\n",
		charCount("Total Sale of the entire store is $[%d]\n"), storeTotal, 0);
		
	LockRelease(globalLock);
}

void Manager() {
	int i, j, k;
	int numClerks;
	int numStockboys;
	int numRegisters;
	int allStockboysOnBreak;
	int allClerksOnBreak;
	/* Total money made from purchases - This figure is the manager's estimate during simulation  */
	int myStoreTotal;
	
	Write("Manager On-Duty.\n", 17, ConsoleOutput);
	while(1)
	{
		Manager_monitorCheckoutAssistanceButton();
		Yield();
		LockAcquire(clerkAssistanceButtonLock);
		if(clerkAssistanceButton == TRUE)
		{
			clerkAssistanceButton = FALSE;
			Write("Manager got a call from Clerk [%d].\n", charCount("Manager got a call from Clerk [%d].\n"),
					ConsoleOutput);
			
			LockAcquire(globalLock);
			numClerks = clerkCount;
			LockRelease(globalLock);
			
			for( k = 0 ; k < numClerks ; k++)
			{
				LockAcquire(clerkLocks[k]);
				if(clerkState[k] == 1)
				{
					Write("Manager wakes up Clerk.\n", charCount("Manager wakes up Clerk.\n"), ConsoleOutput);
					CVSignal(clerkCV[k], clerkLocks[k]);
					LockRelease(clerkLocks[k]);
					continue;
				}
				LockRelease(clerkLocks[k]);
			}
		}
		LockRelease(clerkAssistanceButtonLock);
		Yield();
		
		Manager_monitorCustomerAssistanceButton();
		Yield();
		
		Manager_monitorShelfInventory();
		Yield();
		
		Manager_monitorCashRegisterAssistanceButton();
		Yield();
		
		j = Rand(0,4);
		if(j == 0) {
			LockAcquire(globalLock);
			numRegisters = cashRegisterCount;
			LockRelease(globalLock);
	
			myStoreTotal = 0;
	
			for(i=0; i<numRegisters; i++) {
				LockAcquire(globalLock);
		
				myStoreTotal += cashRegisterSales[i];
				cashRegisterAmount[i] = 0;
				SWrite("Manager emptied Cash Register [%d] drawer.\n",
					charCount("Manager emptied Cash Register [%d] drawer.\n"), i, 0);
				SWrite("Manager has total sale of $[%d].\n", charCount("Manager has total sale of $[%d].\n"), myStoreTotal, 0);
				SWrite("Cash Register [%d] drawer is emptied by Manager.\n",
					charCount("Cash Register [%d] drawer is emptied by Manager.\n"), i, 0);
				SWrite("Cash Register [%d] total sale until now is $[%d].\n",
					charCount("Cash Register [%d] total sale until now is $[%d].\n"), i, cashRegisterSales[i]);
		
				LockRelease(globalLock);
				CVSignal(cashRegisterAssistanceCV[i], cashRegisterAssistanceLock);
				LockRelease(cashRegisterLocks[i]);
			}
		}
		Yield();
		
		
		j = Rand(0,8);
		if(j == 0) {
			LockAcquire(globalLock);
			SWrite("-------Total Sale of the entire store until now is $[%d]---------",
				charCount("-------Total Sale of the entire store until now is $[%d]---------"), storeTotal, 0);
			LockRelease(globalLock);
		}
		Yield();
		
		
		LockAcquire(globalLock);
		if(customerCount > 0 && totalCustomersServed == customerCount) {
			numStockboys = stockboyCount;
			numClerks = clerkCount;
			LockRelease(globalLock);
		
			/* CHECK to see if the stockboys are all on break */
			allStockboysOnBreak = 1;
			for( k = 0 ; k < numStockboys ; k++) {
				LockAcquire(stockboyLocks[k]);
				if(stockboyState[k] != 1) {
					allStockboysOnBreak = 0;
					LockRelease(stockboyLocks[k]);
					break;
				}
				LockRelease(stockboyLocks[k]);
			}
			if(allStockboysOnBreak == 1) {
				/* CHECK to see if the stockboys are all on break */
				allClerksOnBreak = 1;
				for( k = 0 ; k < numClerks ; k++) {
					LockAcquire(clerkLocks[k]);
					if(clerkState[k] != 1) {
						allClerksOnBreak = 0;
						LockRelease(clerkLocks[k]);
						break;
					}
					LockRelease(clerkLocks[k]);
				}
				if(allClerksOnBreak == 1) {
					/* CLOSE HOME DEPOT */
					Write("\n", 1, ConsoleOutput);
					Write("-----H O M E   D E P O T   C L O S E D-----\n",
						charCount("-----H O M E   D E P O T   C L O S E D-----\n"), ConsoleOutput);
					Manager_printTotals();
					break;
				}
			}
			
		}
		else {
			LockRelease(globalLock);
		}
		Yield();
	}
	
	Exit(-1);
}

int main()  {
	/*int sizesArray[] = {50, 0, 7};
	char intString[6];
	char mystring[50];
	int out = 22;
	ToString(intString, 2, out);
	Write(intString, 6, ConsoleOutput);
	Write("\n", 1, ConsoleOutput);
		
	out = Append(mystring, "This is", sizesArray);
	Write(mystring,  out, ConsoleOutput);
	char intString[6];*/
	int i;
	Initialize();
	Write("Home Depot starting...\n", charCount("Home Depot starting...\n"), ConsoleOutput);
	/*for(i=0; i< 4; i++) {
		Fork(&Customer, "Customer", 8);
	}*/
	

	Fork(&Clerk, "Clerk0", 6);
	Fork(&CashRegister, "Cash Register0", 14);
	Fork(&Clerk, "Clerk1", 6);
	Fork(&CashRegister, "Cash Register1", 14);
	
	Fork(&StockBoy, "StockBoy0", 9);
	Fork(&StockBoy, "StockBoy1", 9);
	Fork(&StockBoy, "StockBoy2", 9);
	Fork(&StockBoy, "StockBoy3", 9);
	Fork(&StockBoy, "StockBoy4", 9);
	
	Fork(&Manager, "Manager", 7);
	
	Fork(&Customer, "Customer0", 9);
	Fork(&Customer, "Customer1", 9);
	Fork(&Customer, "Customer2", 9);
	Fork(&Customer, "Customer3", 9);
	Fork(&Customer, "Customer4", 9);
	Fork(&Customer, "Customer5", 9);
	
	
	Write("Forks done.\n", 12, ConsoleOutput);
}


/*void Trace(char *entity, char *message, int value1, int value2) {
	char buf[]

}*/